use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
use lazy_static::lazy_static;
use crate::println;
use crate::init::pic::{PIC_1_OFFSET, PICS};

/// Interrupt descriptor table initialization and handlers
mod idt {
    use super::*;

    #[derive(Debug, Clone, Copy)]
    #[repr(u8)]
    pub enum InterruptIndex {
        Timer = PIC_1_OFFSET,
        Keyboard,
    }

    impl InterruptIndex {
        pub fn as_u8(self) -> u8 {
            self as u8
        }

        pub fn as_usize(self) -> usize {
            usize::from(self.as_u8())
        }
    }

    lazy_static! {
        static ref IDT: InterruptDescriptorTable = {
            let mut idt = InterruptDescriptorTable::new();
            idt.breakpoint.set_handler_fn(breakpoint_handler);
            unsafe {
                idt.double_fault.set_handler_fn(double_fault_handler)
                    .set_stack_index(0);
            }
            idt[InterruptIndex::Timer.as_u8()]
                .set_handler_fn(timer_interrupt_handler);
            idt[InterruptIndex::Keyboard.as_u8()]
                .set_handler_fn(keyboard_interrupt_handler);
            idt
        };
    }

    pub fn init() {
        IDT.load();
    }

    extern "x86-interrupt" fn double_fault_handler(
        stack_frame: InterruptStackFrame, _error_code: u64) -> !
    {
        panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
    }

    extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
        println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
    }

    extern "x86-interrupt" fn timer_interrupt_handler(
        _stack_frame: InterruptStackFrame)
    {
        crate::driver::timer::handle_interrupt();
    }

    extern "x86-interrupt" fn keyboard_interrupt_handler(
        _stack_frame: InterruptStackFrame)
    {
        crate::driver::keyboard::handle_interrupt();
    }
}

pub use idt::{init, InterruptIndex};
